local skynet = require "skynet"
local cluster = require "skynet.cluster"
local servicepoolmng = require "incrservicepoolmng"
local configdao = require "configdao"
local base = require "base"
local timetool = require "timetool"
local routerbase = require "routerbase"
local filelog = require "filelog"
local filename = "router.lua"
require "skynet.manager"
require "pubenum"

local router = routerbase:new({
	idle_table_mng = nil,
	train_index_begin = 20000,
	used_table_pool = {},
	table_index_begin = 21000,
	table_service_map = {},
	index_to_table = {},
	code_to_index = {},
	playermap = {},
	public_list = {},
	my_list = {},
	train_list = {},
	match_list = {},	----服务器匹配场队列
	player_call_state = {},
	----游戏服务器即将停止(在桌子外的玩家不能创建游戏,在桌子内的玩家完成本局游戏离开牌桌到大厅)
	will_stop_server = false,
	})

function router:init()
	self:add_func("nodemsg/nodecmd")
	self:add_func("nodemsg/noderequest")
	self:add_func("nodemsg/matchtable")		---加入匹配场逻辑
	self:add_func("nodemsg/coinmatchtable")	---加入友豆匹配场逻辑
	skynet.fork(function() 
		while true do
			local delmaps = {}
			for rid, player_map in pairs(router.playermap) do
				if player_map.action_time + 300 <= timetool.get_time() then
					table.insert(delmaps, rid)
				end
			end
			for index, rid in ipairs(delmaps) do
				if router.playermap[rid] then
					local status, result = skynet.pcall(skynet.call, router.playermap[rid].table_service, "lua", "clear_player_state", rid)
					if not status then
						router:unforward(rid, router.playermap[rid].table_index)
					end
				end
			end
			router.CMD["check_add_tablerobot"]()
			skynet.sleep(5 * 100)
		end
	end)
end

function router:forward(rid, tableservice, id)
	if self.playermap[rid] == nil then
		self.playermap[rid] = { table_service = tableservice , table_index = id, action_time = timetool.get_time() }
		self:notice_tableinfo_togatenode("role_enter_table", rid, skynet.getenv("nodename"), id)
	else
		self.playermap[rid].table_service = tableservice
		self.playermap[rid].table_index = id
		self:notice_tableinfo_togatenode("role_enter_table", rid, skynet.getenv("nodename"), id)
	end
end

function router:forward_train(rid, tableservice, id)
	if self.playermap[rid] == nil then
		self.playermap[rid] = { table_service = tableservice , table_index = id, action_time = timetool.get_time() }
	else
		self.playermap[rid].table_service = tableservice
		self.playermap[rid].table_index = id
	end
end

function router:unforward(rid, id)
	if self.playermap[rid] then
		if self.playermap[rid].table_index == id then
			self.playermap[rid] = nil
		end
	end
	if id ~= 0 then
		self:notice_tableinfo_togatenode("role_leave_table", rid, skynet.getenv("nodename"), id)
	end
end

function router:unforward_train(rid, id)
	if self.playermap[rid] then
		if self.playermap[rid].table_index == id then
			self.playermap[rid] = nil
		end
	end
end

function router:clear_player_state(rid)
	if self.playermap[rid] then
		self.playermap[rid] = nil
	end
end

function router:get_server_flag()
	return self.will_stop_server
end

function router:set_server_flag(flag)
	self.will_stop_server = flag	
	if self.will_stop_server == true then
		for index, table_service_obj in pairs(self.index_to_table) do
			if table_service_obj.table_service ~= nil then
				pcall(skynet.send, table_service_obj.table_service, "lua", "update_stop_server", flag)
			end
		end
	end
end

function router:set_idle_table_pool(conf)
	self.idle_table_mng = servicepoolmng:new({}, {service_name="table", service_size=conf.tablesize, incr=conf.tableinrc})
end

function router:get_gamenode_codes()
	return self.index_to_table
end

function router:get_gamenode_data()
	return self.table_service_map
end

function router:get_role_data(rid)
	return self.playermap[rid]
end

function router:get_match_list()
	return self.match_list
end

function router:delete_table(id, rids)
	if id == nil or self.used_table_pool[id] ==  nil then return end
	local tableinfo = self.used_table_pool[id]
	local table_index = nil
	local code = tableinfo.create_table_id
	if tableinfo ~= nil then
		if tableinfo.create_table_id ~= nil then
			table_index = self.code_to_index[tableinfo.create_table_id]
			self.index_to_table[self.code_to_index[tableinfo.create_table_id]] = nil
			self.code_to_index[tableinfo.create_table_id] = nil
		end
		self.used_table_pool[id] = nil
	end
	if rids and type(rids) == "table" then
		for index, rid in ipairs(rids) do
			self:unforward(rid, id)
		end
	end
	if table_index and self.public_list[table_index] then
		self.public_list[table_index] = nil
	end
	if table_index and self.my_list[table_index] then
		self.my_list[table_index] = nil
	end
	if table_index and self.match_list[table_index] then
		self.match_list[table_index] = nil
	end
	if id >= 21000 then
		if table_index == nil then table_index = id end
		cluster.send("hallnode", ".router", "del_table_code", code, skynet.getenv("nodename"), table_index)
	elseif id >20000 and id < 21000 then
		self.train_index_begin = self.train_index_begin - 1
	end
	return true
end

function router:update_table_player(table_index, num, table_type, match_type, robot_num)
	if table_type == DNTableType.TABLE_TYPE_ZUANSI or table_type == DNTableType.TABLE_TYPE_FANGKA then
		if table_index and self.public_list[table_index] then
			self.public_list[table_index].cur_player = num
			self.public_list[table_index].robot_num = robot_num
		end
		if table_index and self.my_list[table_index] then
			self.my_list[table_index].cur_player = num
			self.my_list[table_index].robot_num = robot_num
		end
	elseif table_type == DNTableType.TABLE_TYPE_TRAIN then
		if table_index and self.train_list[table_index] then
			self.train_list[table_index].cur_player = num
			self.train_list[table_index].robot_num = robot_num
		end
	end

	if match_type ~= nil and match_type ~= 0 then
		if table_index and self.match_list[table_index] then
			self.match_list[table_index].cur_player = num
			self.match_list[table_index].robot_num = robot_num
		end
	end
end

function router:create_friend_table(conf)
	local tableservice = self.idle_table_mng:create_service()
	local create_table_id
	local tableinfo
	if tableservice ~= nil then
		---生成随机码
		conf.table_index = self.table_index_begin
		self.table_index_begin = self.table_index_begin + 1
		local newcode = cluster.call("hallnode", ".router", "create_table_code", skynet.getenv("nodename"), conf.table_index, conf.create_user_rid, conf)
		create_table_id = newcode
		self.used_table_pool[conf.table_index] = {}
		tableinfo = self.used_table_pool[conf.table_index]
		tableinfo.table_service = tableservice.service
		tableinfo.isdelete = false
		tableinfo.table_service_id = tableservice.id
		tableinfo.create_table_id = create_table_id
		conf.create_table_id = create_table_id
		conf.create_time = timetool.get_time()
		conf.uuid = conf.uuid .. skynet.getenv("nodename") .. "_" .. tostring(create_table_id) .. "_" .. tostring(conf.table_index) .. "_" .. tostring(timetool.get_time())
		local result = skynet.call(tableinfo.table_service, "lua", "start", conf, skynet.getenv("nodename"))
		if not result then
			filelog.sys_error("create_friend_table(:"..self.table_index_begin..") failed")
			pcall(skynet.kill, tableinfo.table_service)
			self.used_table_pool[conf.table_index] = nil
			self.index_to_table[conf.table_index] = nil
			self.code_to_index[create_table_id] = nil
			if self.table_index_begin > 1 then
				self.table_index_begin = self.table_index_begin - 1
			end
			return false, create_table_id, conf.table_index
		end	
	else
		filelog.sys_error("create_friend_table roomsvr's idle_table_mng not enough tableservice!")
		return false, create_table_id, conf.table_index
	end
	self.code_to_index[create_table_id] = conf.table_index
	self.index_to_table[conf.table_index] = {
		create_table_id = create_table_id,
    	table_service = tableinfo.table_service,
    	id = conf.table_index,
	}
	self.table_service_map[create_table_id] = {
		table_index = conf.table_index,
		table_service = tableinfo.table_service,
		create_rid = conf.create_user_rid,
		conf = conf,
		create_time = conf.create_time,
	}
	if conf.enter_type == DNTableEnter.TABLE_ENTER_PUBLIC then
		self.public_list[conf.table_index] = {
			table_index		= conf.table_index,
			table_code		= conf.create_table_id,
			base_score 		= conf.score_diamond,
			enter_diamond	= conf.enter_diamond,
			leave_diamond	= conf.leave_diamond,
			conf_intro		= {mode = conf.game_type},
			max_player		= conf.max_player_num,
			cur_player		= 0,
			create_time		= conf.create_time,
			create_rid 		= conf.create_user_rid,
			match_type		= conf.match_type,
			table_type		= conf.table_type,
			robot_num		= 0,
			is_public		= conf.is_public,
		}
	else
		if conf.match_type == 0 then
			self.my_list[conf.table_index] = {
				table_index		= conf.table_index,
				table_code		= conf.create_table_id,
				base_score 		= conf.score_diamond,
				enter_diamond	= conf.enter_diamond,
				leave_diamond	= conf.leave_diamond,
				conf_intro		= {mode = conf.game_type},
				max_player		= conf.max_player_num,
				cur_player		= 0,
				create_time		= conf.create_time,
				create_rid 		= conf.create_user_rid,
				match_type		= 0,
				table_type		= conf.table_type,
				robot_num 		= 0,
				is_public		= conf.is_public,
			}
		else
			self.match_list[conf.table_index] = {
				table_index		= conf.table_index,
				table_code		= conf.create_table_id,
				base_score 		= conf.score_diamond,
				enter_diamond	= conf.enter_diamond,
				leave_diamond	= conf.leave_diamond,
				conf_intro		= {mode = conf.game_type},
				max_player		= conf.max_player_num,
				cur_player		= 0,
				create_time		= conf.create_time,
				create_rid 		= conf.create_user_rid,
				match_type		= conf.match_type,
				table_type		= conf.table_type,
				robot_num		= 0,
				is_public		= conf.is_public,
			}
		end
	end
	return true, create_table_id, conf.table_index
end

function router:get_playernum(egameroomtype)
	local cur_players = 0 
	if egameroomtype == EGameRoomType.ROOM_TYPE_ZIJIAN_FANGKA then
		for k, list in pairs(self.public_list) do
			if list.table_type == DNTableType.TABLE_TYPE_FANGKA then
				cur_players = cur_players + list.cur_player
			end
		end
		for k, list in pairs(self.my_list) do
			if list.table_type == DNTableType.TABLE_TYPE_FANGKA then
				cur_players = cur_players + list.cur_player
			end
		end
	elseif egameroomtype == EGameRoomType.ROOM_TYPE_ZIJIAN_ZUANSI then
		for k, list in pairs(self.public_list) do
			if list.table_type == DNTableType.TABLE_TYPE_ZUANSI and list.match_type == 0 then
				cur_players = cur_players + list.cur_player
			end
		end

		for k, list in pairs(self.my_list) do
			if list.table_type == DNTableType.TABLE_TYPE_ZUANSI and list.match_type == 0 then
				cur_players = cur_players + list.cur_player
			end
		end
	elseif egameroomtype == EGameRoomType.ROOM_TYPE_ZIJIAN_COIN then
		for k, list in pairs(self.public_list) do
			if list.table_type == DNTableType.TABLE_TYPE_COIN and list.match_type == 0 then
				cur_players = cur_players + list.cur_player
			end
		end
		for k, list in pairs(self.my_list) do
			if list.table_type == DNTableType.TABLE_TYPE_COIN and list.match_type == 0 then
				cur_players = cur_players + list.cur_player
			end
		end
	end

	return cur_players
end


function router:notice_tableinfo_togatenode(msgname, rid, gamenode, table_index)
	cluster.call("gatenode", ".router", msgname, rid, gamenode, table_index)
end


skynet.start(function()
	skynet.dispatch("lua", 
		function (session, source, command, rid, ...)
			local status = true, result
			if router.playermap[rid] and router.playermap[rid].table_service then
				status, result = skynet.pcall(skynet.call, router.playermap[rid].table_service, "lua", command, rid, ...)
				if status then
					if router.playermap[rid] ~= nil and router.playermap[rid].action_time ~= nil then
						router.playermap[rid].action_time = timetool.get_time()
					end
				else
					filelog.sys_error("call table server failed!", command, rid)
					result = {retcode = EErrCode.ERR_SYSTEM_ERROR, retstr = "ps系统错误！"}
				end
				skynet.retpack(result)
			else
				if command == "entertable" then
					if router.player_call_state[rid] == nil then router.player_call_state[rid] = false end
					local f = router.CMD[command]
					if f == nil then
						filelog.sys_error(filename.." CMD[command] is nil : ", rid, command)
						local result = false
					else
						if type(f) == "function" then
							if router.player_call_state[rid] == false then
								router.player_call_state[rid] = true
								status, result = skynet.pcall(f, rid, ... )
								if not status then
									filelog.sys_error(filename.." CMD[command] skynet.pcall failed : ", command, rid, result, ...)
								end
								router.player_call_state[rid] = false
							end
						else
							filelog.print("role not in game table!")
							result = {retcode = EErrCode.ERR_INVALID_REQUEST, retstr = "玩家没在游戏中！"}
						end
					end
					if router.player_call_state[rid] == false then
						skynet.retpack(result)
					end
				else
					local f = router.CMD[command]
					if f == nil then
						filelog.sys_error(filename.." CMD[command] is nil : ", rid, command)
						local result = false
					else
						if type(f) == "function" then
							status, result = skynet.pcall(f, rid, ... )
							if not status then
								filelog.sys_error(filename.." CMD[command] skynet.pcall failed : ", command, rid, result, ...)
							end
						else
							filelog.print("role not in game table!")
							result = {retcode = EErrCode.ERR_INVALID_REQUEST, retstr = "玩家没在游戏中！"}
						end
					end
					skynet.retpack(result)
				end
			end
		end)
	router:init()
end)
